/**
 * 测试执行引擎
 * 负责执行测试动作并生成测试报告
 */

import { chromium, firefox, webkit, Browser, BrowserContext, Page } from 'playwright';
import { AITestDefinition, TestAction, TestResult } from '../types';
import { parseTemplate, retry, sleep } from '../utils';
import { createContextLogger } from '../utils/logger';
import { parsePrompt } from '../ai';
import dotenv from 'dotenv';

// 加载环境变量
dotenv.config();

// 创建日志记录器
const logger = createContextLogger('TestEngine');

/**
 * 测试执行引擎类
 * 负责执行测试动作并生成测试报告
 */
export class TestEngine {
  private static instance: TestEngine;
  private browser: Browser | null = null;
  private context: BrowserContext | null = null;
  private page: Page | null = null;
  
  private constructor() {}
  
  /**
   * 获取单例实例
   */
  public static getInstance(): TestEngine {
    if (!TestEngine.instance) {
      TestEngine.instance = new TestEngine();
    }
    return TestEngine.instance;
  }
  
  /**
   * 初始化浏览器
   */
  private async initBrowser(): Promise<void> {
    if (this.browser) return;
    
    const browserType = process.env.BROWSER || 'chromium';
    const headless = process.env.HEADLESS !== 'false';
    
    logger.info(`启动${browserType}浏览器，headless模式: ${headless}`);
    
    try {
      switch (browserType.toLowerCase()) {
        case 'firefox':
          this.browser = await firefox.launch({ headless });
          break;
        case 'webkit':
          this.browser = await webkit.launch({ headless });
          break;
        case 'chromium':
        default:
          this.browser = await chromium.launch({ headless });
          break;
      }
      
      this.context = await this.browser.newContext({
        viewport: { width: 1280, height: 720 },
        recordVideo: { dir: './reports/videos/' }
      });
      
      this.page = await this.context.newPage();
      
      // 设置超时
      this.page.setDefaultTimeout(30000);
      
      logger.info('浏览器初始化成功');
    } catch (error) {
      logger.error('浏览器初始化失败', { error });
      throw error;
    }
  }
  
  /**
   * 关闭浏览器
   */
  public async closeBrowser(): Promise<void> {
    if (this.browser) {
      await this.browser.close();
      this.browser = null;
      this.context = null;
      this.page = null;
      logger.info('浏览器已关闭');
    }
  }
  
  /**
   * 执行测试
   * @param test 测试定义对象
   * @returns 测试结果的Promise
   */
  public async runTest(test: AITestDefinition): Promise<TestResult> {
    const startTime = Date.now();
    const testId = `test_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
    const screenshots: string[] = [];
    const logs: string[] = [];
    
    logger.info(`开始执行测试: ${test.scenario}`, { testId });
    
    try {
      // 初始化浏览器
      await this.initBrowser();
      
      if (!this.page) {
        throw new Error('浏览器页面未初始化');
      }
      
      // 如果没有预定义的动作，从提示词生成
      let actions = test.actions;
      if (!actions || actions.length === 0) {
        logger.info('从提示词生成测试动作');
        actions = await parsePrompt(test.prompt, test.baseUrl);
      }
      
      // 执行测试动作
      for (const action of actions) {
        await this.executeAction(action, test.params || {});
      }
      
      const duration = Date.now() - startTime;
      logger.info(`测试执行成功，耗时: ${duration}ms`);
      
      // 返回测试结果
      return {
        testId,
        scenario: test.scenario,
        success: true,
        duration,
        screenshots,
        logs,
        metadata: {
          browser: process.env.BROWSER || 'chromium',
          timestamp: new Date().toISOString()
        }
      };
    } catch (error: any) {  // 或者使用 catch (error: Error)
      const duration = Date.now() - startTime;
      logger.error(`测试执行失败: ${error.message}`, { error });
      
      // 截图记录错误
      if (this.page) {
        const screenshotPath = `./reports/screenshots/error_${testId}.png`;
        await this.page.screenshot({ path: screenshotPath });
        screenshots.push(screenshotPath);
      }
      
      // 返回失败结果
      return {
        testId,
        scenario: test.scenario,
        success: false,
        error: error.message || '未知错误',  // 添加默认值
        duration,
        screenshots,
        logs,
        metadata: {
          browser: process.env.BROWSER || 'chromium',
          timestamp: new Date().toISOString()
        }
      };
    }
  }
  
  /**
   * 执行单个测试动作
   * @param action 测试动作
   * @param params 测试参数
   */
  private async executeAction(action: TestAction, params: Record<string, any>): Promise<void> {
    if (!this.page) {
      throw new Error('浏览器页面未初始化');
    }
    
    logger.info(`执行动作: ${action.type}`, { action });
    
    // 解析模板值
    const context = { ...params, page: this.page };
    const selector = action.selector ? parseTemplate(action.selector, context) : undefined;
    const value = action.value ? parseTemplate(action.value, context) : undefined;
    
    // 根据动作类型执行不同操作
    switch (action.type) {
      case 'navigate':
        if (!value) throw new Error('navigate动作缺少URL值');
        await this.page.goto(value.startsWith('http') ? value : `${params.baseUrl || ''}${value}`);
        break;
        
      case 'click':
        if (!selector) throw new Error('click动作缺少选择器');
        await retry(() => this.page!.click(selector), { 
          attempts: 3,
          onRetry: (attempt) => logger.warn(`点击元素重试 (${attempt}/3): ${selector}`)
        });
        break;
        
      case 'fill':
        if (!selector) throw new Error('fill动作缺少选择器');
        if (value === undefined) throw new Error('fill动作缺少值');
        await this.page.fill(selector, value);
        break;
        
      case 'select':
        if (!selector) throw new Error('select动作缺少选择器');
        if (value === undefined) throw new Error('select动作缺少值');
        await this.page.selectOption(selector, value);
        break;
        
      case 'hover':
        if (!selector) throw new Error('hover动作缺少选择器');
        await this.page.hover(selector);
        break;
        
      case 'press':
        if (!value) throw new Error('press动作缺少键值');
        if (selector) {
          await this.page.press(selector, value);
        } else {
          await this.page.keyboard.press(value);
        }
        break;
        
      case 'wait':
        if (selector) {
          await this.page.waitForSelector(selector, { timeout: parseInt(value || '30000') });
        } else if (value) {
          await sleep(parseInt(value));
        } else {
          throw new Error('wait动作缺少选择器或等待时间');
        }
        break;
        
      case 'assert':
        await this.executeAssertion(action.condition || '', selector, value);
        break;
        
      case 'screenshot':
        const screenshotPath = `./reports/screenshots/${Date.now()}.png`;
        await this.page.screenshot({ path: screenshotPath });
        logger.info(`截图已保存: ${screenshotPath}`);
        break;
        
      case 'custom':
        // 自定义动作，需要在params中提供处理函数
        if (!value) {
          throw new Error('custom动作缺少处理函数名称');
        }
        if (params.customActions && typeof params.customActions[value] === 'function') {
          await params.customActions[value](this.page, selector, action.params);
        } else {
          throw new Error(`未找到自定义动作处理函数: ${value}`);
        }
        break;
        
      default:
        throw new Error(`不支持的动作类型: ${action.type}`);
    }
    
    // 每个动作后短暂等待，确保页面响应
    await sleep(action.params?.delay || 500);
  }
  
  /**
   * 执行断言
   * @param condition 断言条件
   * @param selector 选择器
   * @param value 预期值
   */
  private async executeAssertion(condition: string, selector?: string, value?: string): Promise<void> {
    if (!this.page) {
      throw new Error('浏览器页面未初始化');
    }
    
    logger.info(`执行断言: ${condition}`);
    
    try {
      // 解析断言条件
      if (condition.includes('存在')) {
        // 元素存在性断言
        const elementSelector = selector || condition.split('存在')[0].trim();
        await this.page.waitForSelector(elementSelector, { state: 'attached' });
      } else if (condition.includes('可见')) {
        // 元素可见性断言
        const elementSelector = selector || condition.split('可见')[0].trim();
        await this.page.waitForSelector(elementSelector, { state: 'visible' });
      } else if (condition.includes('包含')) {
        // 文本包含断言
        const [elementSelector, expectedText] = condition.split('包含').map(s => s.trim());
        const actualText = await this.page.textContent(elementSelector || selector || '');
        if (!actualText || !actualText.includes(expectedText || value || '')) {
          throw new Error(`断言失败: 文本 "${actualText}" 不包含 "${expectedText || value}"`);
        }
      } else if (condition.includes('等于')) {
        // 值相等断言
        const [elementSelector, expectedValue] = condition.split('等于').map(s => s.trim());
        const actualValue = await this.page.inputValue(elementSelector || selector || '');
        if (actualValue !== (expectedValue || value)) {
          throw new Error(`断言失败: 值 "${actualValue}" 不等于 "${expectedValue || value}"`);
        }
      } else if (condition.includes('url')) {
        // URL断言
        const expectedUrlPart = condition.split('url')[1].includes('包含') 
          ? condition.split('包含')[1].trim()
          : value;
          
        const currentUrl = this.page.url();
        if (!currentUrl.includes(expectedUrlPart || '')) {
          throw new Error(`断言失败: URL "${currentUrl}" 不包含 "${expectedUrlPart}"`);
        }
      } else {
        // 默认作为选择器处理
        await this.page.waitForSelector(condition || selector || '', { state: 'visible' });
      }
      
      logger.info('断言通过');
    } catch (error: any) {  // 或者使用 catch (error: Error)
      logger.error(`断言失败: ${error.message || '未知错误'}`);
      throw new Error(`断言失败: ${error.message || '未知错误'}`);
    }
  }
}

/**
 * 执行测试的快捷函数
 * @param test 测试定义对象
 * @returns 测试结果的Promise
 */
export async function runTest(test: AITestDefinition): Promise<TestResult> {
  return await TestEngine.getInstance().runTest(test);
}